import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:private_journey/States.dart';
import 'package:private_journey/services/SearchService.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' as shadcn;
import 'package:signals/signals_flutter.dart';
import 'package:url_launcher/url_launcher.dart';

class SearchComponent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return SearchComponentState();
  }
}

class SearchComponentState extends State<SearchComponent> {
  @override
  Widget build(BuildContext context) {
    return shadcn.TextField(
      placeholder: const shadcn.Text('search everything').black.italic,

      onSubmitted: (s) {
        if (currentSearchEngine.value == 'Bing') {
          launchUrl(Uri.parse('https://www.bing.com/search?q=$s'));
        }
        if (currentSearchEngine.value == 'Baidu') {
          launchUrl(Uri.parse('https://www.baidu.com/s?wd=$s'));
        }
        if (currentSearchEngine.value == 'Google') {
          launchUrl(Uri.parse('https://www.google.com/search?q=$s'));
        }
      },
      features: [
        InputAutoCompleteFeature(
          visibility: shadcn.InputFeatureVisibility.always,
          querySuggestions: (String query) {
            return SearchService.fetchWordGroup(query);
          },
          mode: shadcn.AutoCompleteMode.replaceAll,
          child: shadcn.Text('自动完成建议'),
        ),
        shadcn.InputLeadingFeature(
          Watch((context) {
            return shadcn.Select<String>(
              itemBuilder: (context, item) {
                if (item == 'Bing') {
                  return SvgPicture.asset(
                    'assets/svg/bing.svg',
                    height: 25,
                    width: 25,
                  );
                }
                if (item == 'Baidu') {
                  return SvgPicture.asset(
                    'assets/svg/baidu.svg',
                    height: 25,
                    width: 25,
                  );
                }
                if (item == 'Google') {
                  return SvgPicture.asset(
                    'assets/svg/google.svg',
                    height: 25,
                    width: 25,
                  );
                }
                return Text(item);
              },
              popupConstraints: const BoxConstraints(
                maxHeight: 300,
                maxWidth: 145,
              ),
              popupWidthConstraint: shadcn.PopoverConstraint.flexible,
              onChanged: (value) {
                currentSearchEngine.value = value!;
              },
              value: currentSearchEngine.value,
              popup: shadcn.SelectPopup(
                items: shadcn.SelectItemList(
                  children: [
                    shadcn.SelectItemButton(
                      value: 'Bing',
                      child: Row(
                        spacing: 10,
                        children: [
                          SvgPicture.asset(
                            'assets/svg/bing.svg',
                            height: 25,
                            width: 25,
                          ),
                          shadcn.Text('Bing'),
                        ],
                      ),
                    ),
                    shadcn.SelectItemButton(
                      value: 'Baidu',
                      child: Row(
                        spacing: 10,
                        children: [
                          SvgPicture.asset(
                            'assets/svg/baidu.svg',
                            height: 25,
                            width: 25,
                          ),
                          shadcn.Text('Baidu'),
                        ],
                      ),
                    ),
                    shadcn.SelectItemButton(
                      value: 'Google',
                      child: Row(
                        spacing: 10,
                        children: [
                          SvgPicture.asset(
                            'assets/svg/google.svg',
                            height: 25,
                            width: 25,
                          ),
                          shadcn.Text('Google'),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            );
          }),
        ),
      ],
    );
  }
}

class InputAutoCompleteFeature extends shadcn.InputFeature {
  final shadcn.SuggestionBuilder querySuggestions;
  final Widget child;
  final BoxConstraints? popoverConstraints;
  final shadcn.PopoverConstraint? popoverWidthConstraint;
  final AlignmentDirectional? popoverAnchorAlignment;
  final AlignmentDirectional? popoverAlignment;
  final shadcn.AutoCompleteMode mode;

  const InputAutoCompleteFeature({
    super.visibility,
    required this.querySuggestions,
    required this.child,
    this.popoverConstraints,
    this.popoverWidthConstraint,
    this.popoverAnchorAlignment,
    this.popoverAlignment,
    this.mode = shadcn.AutoCompleteMode.replaceWord,
  });

  @override
  shadcn.InputFeatureState createState() => _AutoCompleteFeatureState();
}

class _AutoCompleteFeatureState
    extends shadcn.InputFeatureState<InputAutoCompleteFeature> {
  final GlobalKey _key = GlobalKey();
  final ValueNotifier<FutureOr<Iterable<String>>?> _suggestions = ValueNotifier(
    null,
  );

  @override
  void onTextChanged(String text) {
    _suggestions.value = feature.querySuggestions(text);
  }

  @override
  Widget wrap(Widget child) {
    return ListenableBuilder(
      listenable: _suggestions,
      builder: (context, child) {
        var suggestions = _suggestions.value;
        if (suggestions is Future<Iterable<String>>) {
          return FutureBuilder(
            future: suggestions,
            builder: (context, snapshot) {
              return AutoComplete(
                key: _key,
                suggestions:
                    snapshot.hasData ? snapshot.requireData.toList() : const [],
                popoverConstraints: feature.popoverConstraints,
                popoverWidthConstraint: feature.popoverWidthConstraint,
                popoverAnchorAlignment: feature.popoverAnchorAlignment,
                popoverAlignment: feature.popoverAlignment,
                mode: feature.mode,
                child: child!,
              );
            },
          );
        }
        return AutoComplete(
          key: _key,
          suggestions: suggestions == null ? const [] : suggestions.toList(),
          popoverConstraints: feature.popoverConstraints,
          popoverWidthConstraint: feature.popoverWidthConstraint,
          popoverAnchorAlignment: feature.popoverAnchorAlignment,
          popoverAlignment: feature.popoverAlignment,
          mode: feature.mode,
          child: child!,
        );
      },
      child: child,
    );
  }
}

class AutoComplete extends StatefulWidget {
  final List<String> suggestions;
  final Widget child;
  final BoxConstraints? popoverConstraints;
  final shadcn.PopoverConstraint? popoverWidthConstraint;
  final AlignmentDirectional? popoverAnchorAlignment;
  final AlignmentDirectional? popoverAlignment;
  final shadcn.AutoCompleteMode mode;
  final shadcn.AutoCompleteCompleter completer;

  const AutoComplete({
    super.key,
    required this.suggestions,
    required this.child,
    this.popoverConstraints,
    this.popoverWidthConstraint,
    this.popoverAnchorAlignment,
    this.popoverAlignment,
    this.mode = shadcn.AutoCompleteMode.replaceWord,
    this.completer = _defaultCompleter,
  });

  @override
  State<AutoComplete> createState() => _AutoCompleteState();

  static String _defaultCompleter(String suggestion) {
    return suggestion;
  }
}

class _AutoCompleteState extends State<AutoComplete> {
  final ValueNotifier<List<String>> _suggestions = ValueNotifier([]);
  final ValueNotifier<int> _selectedIndex = ValueNotifier(-1);
  final shadcn.PopoverController _popoverController =
      shadcn.PopoverController();
  bool _isFocused = false;

  @override
  void initState() {
    super.initState();
    _suggestions.addListener(_onSuggestionsChanged);
    if (widget.suggestions.isNotEmpty) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted) {
          return;
        }
        _suggestions.value = widget.suggestions;
        _selectedIndex.value = widget.suggestions.isEmpty ? -1 : 0;
      });
    }
  }

  void _onSuggestionsChanged() {
    if ((_suggestions.value.isEmpty && _popoverController.hasOpenPopover) ||
        !_isFocused) {
      _popoverController.close();
    } else if (!_popoverController.hasOpenPopover &&
        _suggestions.value.isNotEmpty) {
      _selectedIndex.value = -1;
      _popoverController.show(
        context: context,
        handler: const shadcn.PopoverOverlayHandler(),
        builder: (context) {
          final theme = shadcn.Theme.of(context);
          return TextFieldTapRegion(
            child: ConstrainedBox(
              constraints:
                  widget.popoverConstraints ??
                  BoxConstraints(maxHeight: 300 * theme.scaling),
              child: shadcn.SurfaceCard(
                padding: const EdgeInsets.all(4) * theme.scaling,
                child: AnimatedBuilder(
                  animation: Listenable.merge([_suggestions, _selectedIndex]),
                  builder: (context, child) {
                    return ListView.builder(
                      shrinkWrap: true,
                      itemCount: _suggestions.value.length,
                      itemBuilder: (context, index) {
                        final suggestion = _suggestions.value[index];
                        return _AutoCompleteItem(
                          suggestion: suggestion,
                          selected: index == _selectedIndex.value,
                          onSelected: () {
                            _selectedIndex.value = index;
                            _handleProceed();
                          },
                        );
                      },
                    );
                  },
                ),
              ),
            ),
          );
        },
        widthConstraint:
            widget.popoverWidthConstraint ??
            shadcn.PopoverConstraint.anchorFixedSize,
        anchorAlignment:
            widget.popoverAnchorAlignment ?? AlignmentDirectional.bottomStart,
        alignment: widget.popoverAlignment ?? AlignmentDirectional.topStart,
      );
    }
  }

  void _handleProceed() {
    final selectedIndex = _selectedIndex.value;
    if (selectedIndex < 0 || selectedIndex >= _suggestions.value.length) {
      return;
    }
    _popoverController.close();
    var suggestion = _suggestions.value[selectedIndex];
    suggestion = widget.completer(suggestion);
    switch (widget.mode) {
      case shadcn.AutoCompleteMode.append:
        shadcn.TextFieldAppendTextIntent intent =
            shadcn.TextFieldAppendTextIntent(text: suggestion);
        shadcn.invokeActionOnFocusedWidget(intent);
        break;
      case shadcn.AutoCompleteMode.replaceWord:
        shadcn.TextFieldReplaceCurrentWordIntent intent =
            shadcn.TextFieldReplaceCurrentWordIntent(text: suggestion);
        shadcn.invokeActionOnFocusedWidget(intent);
        break;
      case shadcn.AutoCompleteMode.replaceAll:
        shadcn.TextFieldSetTextIntent intent = shadcn.TextFieldSetTextIntent(
          text: suggestion,
        );
        shadcn.invokeActionOnFocusedWidget(intent);
        break;
    }
  }

  @override
  void didUpdateWidget(covariant AutoComplete oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!listEquals(oldWidget.suggestions, widget.suggestions)) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!mounted) {
          return;
        }
        _suggestions.value = widget.suggestions;
        _selectedIndex.value = widget.suggestions.isEmpty ? -1 : 0;
      });
    }
  }

  void _onFocusChanged(bool focused) {
    _isFocused = focused;
    if (!focused) {
      _popoverController.close();
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: _selectedIndex,
      builder: (context, child) {
        return FocusableActionDetector(
          onFocusChange: _onFocusChanged,
          shortcuts:
              _popoverController.hasOpenPopover
                  ? {
                    LogicalKeySet(
                      LogicalKeyboardKey.arrowDown,
                    ): const shadcn.NavigateSuggestionIntent(1),
                    LogicalKeySet(
                      LogicalKeyboardKey.arrowUp,
                    ): const shadcn.NavigateSuggestionIntent(-1),
                    if (widget.suggestions.isNotEmpty &&
                        _selectedIndex.value != -1)
                      LogicalKeySet(LogicalKeyboardKey.tab):
                          const shadcn.AcceptSuggestionIntent(),
                  }
                  : null,
          actions:
              _popoverController.hasOpenPopover
                  ? {
                    shadcn.NavigateSuggestionIntent:
                        CallbackAction<shadcn.NavigateSuggestionIntent>(
                          onInvoke: (intent) {
                            final direction = intent.direction;
                            final selectedIndex = _selectedIndex.value;
                            final suggestions = _suggestions.value;
                            if (suggestions.isEmpty) {
                              return;
                            }
                            final newSelectedIndex =
                                (selectedIndex + direction) %
                                suggestions.length;
                            _selectedIndex.value =
                                newSelectedIndex < 0
                                    ? suggestions.length - 1
                                    : newSelectedIndex;
                            return;
                          },
                        ),
                    shadcn.AcceptSuggestionIntent:
                        CallbackAction<shadcn.AcceptSuggestionIntent>(
                          onInvoke: (intent) {
                            _handleProceed();
                            return;
                          },
                        ),
                  }
                  : null,
          child: widget.child,
        );
      },
    );
  }
}

class _AutoCompleteItem extends StatefulWidget {
  final String suggestion;
  final bool selected;
  final VoidCallback onSelected;

  const _AutoCompleteItem({
    required this.suggestion,
    required this.selected,
    required this.onSelected,
  });

  @override
  State<_AutoCompleteItem> createState() => _AutoCompleteItemState();
}

class _AutoCompleteItemState extends State<_AutoCompleteItem> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color:
          widget.selected
              ? shadcn.Theme.of(context).colorScheme.primary
              : Colors.transparent,
      child: shadcn.SelectedButton(
        value: widget.selected,
        style: shadcn.ButtonStyle.link(),
        alignment: AlignmentDirectional.centerStart,
        onChanged: (value) {
          widget.onSelected();
        },
        child: Text(widget.suggestion),
      ),
    );
  }

  @override
  void didUpdateWidget(covariant _AutoCompleteItem oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.selected != widget.selected) {
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        if (!mounted) {
          return;
        }
        if (widget.selected) {
          Scrollable.ensureVisible(context);
        }
      });
    }
  }
}

bool listEquals<T>(List<T>? a, List<T>? b) {
  if (a == null) {
    return b == null;
  }
  if (b == null || a.length != b.length) {
    return false;
  }
  if (identical(a, b)) {
    return true;
  }
  for (int index = 0; index < a.length; index += 1) {
    if (a[index] != b[index]) {
      return false;
    }
  }
  return true;
}
